/**  
* Copyright (c) 2008, Noel Lynch All rights reserved. Redistribution and use  
* in source and binary forms, with or without modification, are permitted  
* provided that the following conditions are met: Redistributions of source  
* code must retain the above copyright notice, this list of conditions and the  
* following disclaimer. Redistributions in binary form must reproduce the above  
* copyright notice, this list of conditions and the following disclaimer in the  
* documentation and/or other materials provided with the distribution. Neither  
* the name of Noel Lynch nor the names of contributors may be used to  
* endorse or promote products derived from this software without specific prior  
* written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;  
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR  
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF  
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
*/
// Aug 5, 2008

package com.sceneworker.editors.spatialtransformereditor;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.BitSet;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.Timer;

import net.miginfocom.swing.MigLayout;

import com.jme.animation.SpatialTransformer;
import com.jme.animation.SpatialTransformer.PointInTime;
import com.jme.math.Quaternion;
import com.jme.math.TransformQuaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;
import com.myjavautils.swing.MigLayoutUtils;
import com.myjavautils.swing.SimpleAlert;
import com.myjavautils.swing.SwingComponentFactory;
import com.sceneworker.app.globals.SceneWorkerDialogManager;
import com.sceneworker.util.swing.timelineeditor.KeyFrameTimeLinePanel;
import com.sceneworker.util.swing.timelineeditor.KeyFramesPanel;
import com.sceneworker.util.swing.timelineeditor.KeyFramesPanel.KeyFramePanelEventListener;
import com.sceneworker.util.swing.timelineeditor.TimeSliderPanel.TimeSliderListener;


public class SpatialTransformerTimeLinePanel extends JPanel implements KeyFramePanelEventListener, TimeSliderListener {

	protected	SpatialTransformer			mcl_tx;
	protected	PointInTimeEditor			mcl_ptInTimeEditor;
	protected	JPanel						mcl_editPanel;
	protected	KeyFrameTimeLinePanel		mcl_timeLinePanel;
	
	protected	PointInTime					mcl_ptInTimeBuffer;
	
	public SpatialTransformerTimeLinePanel(SpatialTransformer cl_tx) {
		super();
		
		mcl_tx = cl_tx;
		mcl_tx.setActive(false);
		
		setLayout(new MigLayout("inset 5"));
		buildKeyFramePanel();
		
		setMinimumSize(new Dimension(400, 400));
	}

	private	void	buildKeyFramePanel() {
		
		mcl_editPanel = MigLayoutUtils.makeStandardPanel();
		
		mcl_timeLinePanel = new KeyFrameTimeLinePanel(20);
	
		mcl_timeLinePanel.getKeyFramesPanel().setKeyFramePanelListener(this);
		mcl_timeLinePanel.getTimeSliderPanel().setTimeSliderListener(this);
		
		// initialise the key frames
		for(SpatialTransformer.PointInTime lcl_keyFrame : mcl_tx.keyframes) {
			mcl_timeLinePanel.getKeyFramesPanel().addKeyFrame(new Float(lcl_keyFrame.time));
		}
		
		// initialise min and max times of anim
		if(mcl_tx.keyframes.size() > 0) {
			mcl_timeLinePanel.getTimeSliderPanel().setStart(mcl_tx.keyframes.get(0).time);
			mcl_timeLinePanel.getTimeSliderPanel().setEnd(mcl_tx.keyframes.get(mcl_tx.keyframes.size() - 1).time);
		}
		
		mcl_ptInTimeEditor = new PointInTimeEditor(mcl_tx, 0);
		mcl_editPanel.add(mcl_ptInTimeEditor, "width 100%, height 100%");
		
		add(mcl_timeLinePanel, "width 100%, wrap");
		buildButtonPanel();
		add(mcl_editPanel, "width 100%, wrap");
		
	}
	
	private	 void	buildButtonPanel() {
		final	SpatialTransformerTimeLinePanel	lcl_this = this;
		
		final	Timer		lcl_timer = new Timer(100, new ActionListener() {
			public void actionPerformed(ActionEvent cl_e) {
				if(mcl_tx.isActive()) {
					mcl_timeLinePanel.getKeyFramesPanel().setCurTimeSel(mcl_tx.getCurTime());
					mcl_timeLinePanel.getKeyFramesPanel().repaint();
				}
			}
			
		});
		
		final	JButton		lcl_play = SwingComponentFactory.makeAButton("Play");
		final	JButton		lcl_stop = SwingComponentFactory.makeAButton("Stop");
		
		lcl_play.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				mcl_tx.setCurTime(mcl_timeLinePanel.getTimeSliderPanel().getStart());
				mcl_tx.setMinTime(mcl_timeLinePanel.getTimeSliderPanel().getStart());
				mcl_tx.setMaxTime(mcl_timeLinePanel.getTimeSliderPanel().getEnd());
				mcl_tx.setActive(true);
				lcl_play.setEnabled(false);
				lcl_stop.setEnabled(true);
				lcl_timer.start();
			}
		});
		
		
		lcl_stop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				mcl_tx.setActive(false);
				lcl_timer.stop();
				lcl_play.setEnabled(true);
				lcl_stop.setEnabled(false);
			}
		});
		lcl_stop.setEnabled(false);
		
		final	KeyFramePanelEventListener		lcl_listener = this;
		JButton		lcl_newKeyFrame = SwingComponentFactory.makeAButton("New Key Frame");
		lcl_newKeyFrame.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JDialog		lcl_dlg = SceneWorkerDialogManager.inst().makeADialog(SceneWorkerDialogManager.NEW_KEY_FRAME);
				lcl_dlg.add(new NewKeyFramePanel(lcl_listener, lcl_dlg));
				lcl_dlg.pack();
				lcl_dlg.setVisible(true);
			}
		});
		
		MigLayoutUtils.makeRightAlignedButtonBar(this, lcl_play, lcl_stop, lcl_newKeyFrame);
	}
	
	public	SpatialTransformer.PointInTime	getKeyFrameFromTime(float f_t) {
		for(SpatialTransformer.PointInTime lcl_keyFrame : mcl_tx.keyframes) {
			if(lcl_keyFrame.time == f_t) {
				return lcl_keyFrame;
			}
		}
		
		return null;
	}

	public void makeNewKeyFrame(KeyFramesPanel cl_panel) {
		makeNewKeyFrame(cl_panel.getCurTimeSel());
	}
	
	public void makeNewKeyFrame(float f_time) {
		if(mcl_tx.isActive()) {
			SimpleAlert.doAlert("The controller is currently running. Stop the controller and try again.", this);
			return;
		}
		
		Spatial		lcl_spatial = mcl_tx.toChange[0];
		
		if(mcl_tx.keyframes.size() == 0) {
			// first key frame so copy the objects local transformations as the first frame
			mcl_tx.setPosition(0, f_time, new Vector3f(lcl_spatial.getLocalTranslation()));
			mcl_tx.setRotation(0, f_time, new Quaternion(lcl_spatial.getLocalRotation()));
			mcl_tx.setScale(0, f_time, new Vector3f(lcl_spatial.getLocalScale()));
		} else {
			SpatialTransformer.PointInTime		lcl_before = getKeyFrameBefore(f_time);
			if(lcl_before == null) {
				mcl_tx.setPosition(0, f_time, new Vector3f(0, 0, 0));
				mcl_tx.setRotation(0, f_time, new Quaternion());
				mcl_tx.setScale(0, f_time, new Vector3f(1, 1, 1));
			} else {
				// copy the previous key frames settings to the new one
				mcl_tx.setPosition(0, f_time, new Vector3f(lcl_before.look[0].getTranslation()));
				mcl_tx.setRotation(0, f_time, new Quaternion(lcl_before.look[0].getRotation()));
				mcl_tx.setScale(0, f_time, new Vector3f(lcl_before.look[0].getScale()));
			}
		}
		
		mcl_tx.interpolateMissing();
		mcl_timeLinePanel.getKeyFramesPanel().addKeyFrame(f_time);
		mcl_timeLinePanel.getTimeSliderPanel().setStart(mcl_tx.keyframes.get(0).time);
		mcl_timeLinePanel.getTimeSliderPanel().setEnd(mcl_tx.keyframes.get(mcl_tx.keyframes.size() - 1).time);
		mcl_tx.setNewAnimationTimes(f_time, f_time);
		mcl_timeLinePanel.repaint();
		
	}
	
	private		SpatialTransformer.PointInTime	getKeyFrameBefore(float f_time) {
		SpatialTransformer.PointInTime		lcl_beforeTime = null;
		float								lf_lowestTime = Float.MAX_VALUE;
		
		for(SpatialTransformer.PointInTime lcl_ptInTime : mcl_tx.keyframes) {
			if(lcl_ptInTime.time < f_time) {
				float		lf_diffInTimes = f_time - lcl_ptInTime.time;
				
				if(lf_diffInTimes < lf_lowestTime) {
					lcl_beforeTime = lcl_ptInTime;
					lf_lowestTime = lf_diffInTimes;
				}
			}
		}
		
		return lcl_beforeTime;
	}
	
	public void newKeyFrameSelection(KeyFramesPanel cl_panel) {
		mcl_tx.setNewAnimationTimes(cl_panel.getCurTimeSel(), cl_panel.getCurTimeSel());
		
		if(cl_panel.getSelectedKeyFrame() != null) {
			PointInTime		lcl_ptInTime = getKeyFrameFromTime(cl_panel.getSelectedKeyFrame().floatValue());
			
			if(lcl_ptInTime != null) {
				mcl_ptInTimeEditor.setPtInTime(lcl_ptInTime);
				mcl_editPanel.setVisible(true);
				return;
			}
			
		} else {
			mcl_editPanel.setVisible(false);
		}
		
		mcl_editPanel.repaint();
	}
	
	public void deleteKeyFrame(KeyFramesPanel cl_panel) {
		PointInTime		lcl_ptInTime = getKeyFrameFromTime(cl_panel.getSelectedKeyFrame().floatValue());
		
		if(lcl_ptInTime != null) {
			if(mcl_tx.keyframes.size() > 1) {		// always keep 1 key frame
				mcl_editPanel.setVisible(false);
				mcl_tx.keyframes.remove(lcl_ptInTime);
				mcl_tx.interpolateMissing();
				mcl_timeLinePanel.getKeyFramesPanel().removeKeyFrameAtTime(lcl_ptInTime.time);
				mcl_timeLinePanel.repaint();
			}
		}
	}

	public void newTimeSelection(KeyFramesPanel cl_panel) {
		mcl_tx.setNewAnimationTimes(cl_panel.getCurTimeSel(), cl_panel.getCurTimeSel());
		mcl_editPanel.setVisible(false);
	}

	public boolean changeEndTime(float i_newTime, float i_oldTime) {
		//  get the time of the last point in time
		if(mcl_tx.keyframes.size() > 0) {
			if(mcl_tx.keyframes.get(mcl_tx.keyframes.size() - 1).time >= i_newTime) {
				return true;
			}
		}
		
		return false;
	}

	public boolean changeStartTime(float i_newTime, float i_oldTime) {
	//  get the time of the last point in time
		if(mcl_tx.keyframes.size() > 0) {
			if(mcl_tx.keyframes.get(0).time <= i_newTime) {
				return true;
			}
		}
		
		return false;
	}

	public void copyKeyFrame(KeyFramesPanel cl_panel) {
		if(cl_panel.getSelectedKeyFrame() != null) {
			PointInTime		lcl_ptInTime = getKeyFrameFromTime(cl_panel.getSelectedKeyFrame().floatValue());
			
			if(lcl_ptInTime == null) {
				mcl_ptInTimeBuffer = null;
				return; 
			}
			
			copyPtInTimeToBuffer(lcl_ptInTime);
		}
	}
	
	private	void	copyPtInTimeToBuffer(PointInTime cl_ptInTime) {
		// copy the key frame into the buffer
		mcl_ptInTimeBuffer = new SpatialTransformer.PointInTime();
		mcl_ptInTimeBuffer.look = new TransformQuaternion[cl_ptInTime.look.length];
		mcl_ptInTimeBuffer.usedTrans = new BitSet(cl_ptInTime.look.length);
		mcl_ptInTimeBuffer.usedRot = new BitSet(cl_ptInTime.look.length);
		mcl_ptInTimeBuffer.usedScale = new BitSet(cl_ptInTime.look.length);
		
		for(int li_index = 0; li_index < cl_ptInTime.look.length; li_index++) {
			mcl_ptInTimeBuffer.look[li_index] = new TransformQuaternion();
			mcl_ptInTimeBuffer.look[li_index].set(cl_ptInTime.look[li_index]);
			
			mcl_ptInTimeBuffer.usedTrans.set(li_index, cl_ptInTime.usedTrans.get(li_index));
			mcl_ptInTimeBuffer.usedScale.set(li_index, cl_ptInTime.usedScale.get(li_index));
			mcl_ptInTimeBuffer.usedRot.set(li_index, cl_ptInTime.usedRot.get(li_index));
		}
	}

	public void cutKeyFrame(KeyFramesPanel cl_panel) {
		if(cl_panel.getSelectedKeyFrame() != null) {
			PointInTime		lcl_ptInTime = getKeyFrameFromTime(cl_panel.getSelectedKeyFrame().floatValue());
			
			if(lcl_ptInTime == null) {
				mcl_ptInTimeBuffer = null;
				return; 
			}
			
			copyPtInTimeToBuffer(lcl_ptInTime);
		}
		
		deleteKeyFrame(cl_panel);
	}

	public void pasteKeyFrame(KeyFramesPanel cl_panel) {
		if(mcl_ptInTimeBuffer != null) {
			mcl_ptInTimeBuffer.time = cl_panel.getCurTimeSel();
			
			mcl_tx.setPosition(0, cl_panel.getCurTimeSel(), mcl_ptInTimeBuffer.look[0].getTranslation());
			mcl_tx.setRotation(0, cl_panel.getCurTimeSel(), mcl_ptInTimeBuffer.look[0].getRotation());
			mcl_tx.setScale(0, cl_panel.getCurTimeSel(), mcl_ptInTimeBuffer.look[0].getScale());
			mcl_tx.interpolateMissing();
			
			mcl_ptInTimeBuffer = null;
			
			mcl_timeLinePanel.getKeyFramesPanel().addKeyFrame(cl_panel.getCurTimeSel());
			
			mcl_timeLinePanel.repaint();
		}
	}

	public boolean isKeyFrameInBuffer(KeyFramesPanel cl_panel) {
		if(mcl_ptInTimeBuffer != null) {
			return true;
		}
		
		return false;
	}

	
}
